cmake_minimum_required(VERSION 3.12)
project(mpi_tests LANGUAGES C CXX Fortran)

###########################################################################
# 关键1：强制使用Intel MPI的编译器包装器（避免符号冲突）
# 优先查找Intel MPI自带的mpicc/mpicxx/mpif90，而非系统GCC
###########################################################################
find_program(MPI_C_COMPILER NAMES mpicc DOC "Intel MPI C compiler wrapper")
find_program(MPI_CXX_COMPILER NAMES mpicxx DOC "Intel MPI CXX compiler wrapper")
find_program(MPI_Fortran_COMPILER NAMES mpif90 DOC "Intel MPI Fortran compiler wrapper")

# 强制设置CMake编译器（覆盖系统默认GCC）
set(CMAKE_C_COMPILER ${MPI_C_COMPILER} CACHE FILEPATH "Intel MPI C compiler" FORCE)
set(CMAKE_CXX_COMPILER ${MPI_CXX_COMPILER} CACHE FILEPATH "Intel MPI CXX compiler" FORCE)
set(CMAKE_Fortran_COMPILER ${MPI_Fortran_COMPILER} CACHE FILEPATH "Intel MPI Fortran compiler" FORCE)

###########################################################################
# 关键2：查找Intel MPI组件（显式指定，避免混合OpenMPI）
###########################################################################
find_package(MPI REQUIRED COMPONENTS C CXX Fortran)
message(STATUS "Intel MPI 路径: ${MPI_CXX_LIBRARIES}")  # 验证是否为Intel MPI路径

###########################################################################
# 关键3：降级C++标准到CXX11（GCC 4.8.5不支持CXX17，MPI测试无需CXX17新特性）
###########################################################################
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)  # 禁用编译器扩展，保证兼容性

###########################################################################
# 基础配置：Fortran模块路径、CMake政策
###########################################################################
# Fortran模块输出目录（混合语言编译时C需找到Fortran模块）
set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules)
include_directories(${CMAKE_Fortran_MODULE_DIRECTORY})

# 禁用父项目编译选项继承
if(POLICY CMP0079)
    cmake_policy(SET CMP0079 NEW)
endif()

###########################################################################
# 编译选项函数：适配Intel编译器与GCC（Intel MPI可能用icc/ifort或GCC包装）
###########################################################################
function(set_mpi_compile_options TARGET_NAME LANGUAGE)
    if(LANGUAGE STREQUAL "Fortran")
        # 适配Intel Fortran编译器（ifort）和GNU Fortran（gfortran）
        if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3
                -warn all
                -diag-disable 5268,7712  # 屏蔽Intel特定警告
            )
        elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3
                -Wall
                -Wno-maybe-uninitialized
                -Wno-tabs
                -ffree-line-length-none
                -fimplicit-none
                -Wno-error
            )
        endif()
    elseif(LANGUAGE MATCHES "CXX|C")
        # 适配Intel C++编译器（icc）和GNU C++（g++）
        if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3
                -warn all
                -diag-disable 177,383  # 屏蔽Intel特定警告
            )
        elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
            target_compile_options(${TARGET_NAME} PRIVATE
                -O3
                -Wall
                -Wextra
                $<$<COMPILE_LANGUAGE:CXX>:-Weffc++>
                $<$<COMPILE_LANGUAGE:CXX>:-Wno-non-virtual-dtor>
            )
        endif()
    endif()
endfunction()

###########################################################################
# MPI目标生成函数：删除重复定义，确保Intel MPI链接正确
###########################################################################
# Fortran MPI目标（仅1份定义）
function(add_mpi_fortran_executable TARGET_NAME)
    add_executable(${TARGET_NAME} ${ARGN})
    set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_OPTIONS "")
    # 链接Intel MPI Fortran库（MPI::MPI_Fortran已包含Intel MPI依赖）
    target_link_libraries(${TARGET_NAME} PRIVATE MPI::MPI_Fortran)
    set_mpi_compile_options(${TARGET_NAME} "Fortran")
endfunction()

# C++ MPI目标（仅1份定义）
function(add_mpi_cpp_executable TARGET_NAME)
    add_executable(${TARGET_NAME} ${ARGN})
    # 链接Intel MPI C++库（补充MPI::MPI_C确保C依赖完整）
    target_link_libraries(${TARGET_NAME} PRIVATE MPI::MPI_CXX MPI::MPI_C)
    set_mpi_compile_options(${TARGET_NAME} "CXX")
endfunction()

# 混合语言（C/Fortran）MPI目标
function(add_mpi_mixed_executable TARGET_NAME)
    set(FORTRAN_SOURCES)
    set(C_SOURCES)
    foreach(src ${ARGN})
        if(src MATCHES "\\.(f90|F90|f95|F95)$")
            list(APPEND FORTRAN_SOURCES ${src})
        elseif(src MATCHES "\\.(c|C|cpp|CPP)$")
            list(APPEND C_SOURCES ${src})
        endif()
    endforeach()

    # Fortran先编译（确保模块生成后C才能引用）
    add_executable(${TARGET_NAME} ${FORTRAN_SOURCES} ${C_SOURCES})

    # 链接Intel MPI的C+Fortran库
    target_link_libraries(${TARGET_NAME} PRIVATE
        MPI::MPI_Fortran
        MPI::MPI_C
        MPI::MPI_CXX
    )

    target_include_directories(${TARGET_NAME} PRIVATE
        ${CMAKE_Fortran_MODULE_DIRECTORY}
    )

    # Fortran源文件选项
    if(FORTRAN_SOURCES)
        set_source_files_properties(${FORTRAN_SOURCES} PROPERTIES LANGUAGE Fortran)
        if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel")
            set_property(SOURCE ${FORTRAN_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                -O3 -warn all -diag-disable 5268,7712
            )
        elseif(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
            set_property(SOURCE ${FORTRAN_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                -O3 -Wall -Wno-maybe-uninitialized -Wno-tabs -ffree-line-length-none -fimplicit-none -Wno-error
            )
        endif()
    endif()

    # C源文件选项
    if(C_SOURCES)
        set_source_files_properties(${C_SOURCES} PROPERTIES LANGUAGE C)
        if(CMAKE_C_COMPILER_ID MATCHES "Intel")
            set_property(SOURCE ${C_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                -O3 -warn all -diag-disable 177,383
            )
        elseif(CMAKE_C_COMPILER_ID MATCHES "GNU")
            set_property(SOURCE ${C_SOURCES} APPEND PROPERTY COMPILE_OPTIONS
                -O3 -Wall -Wextra
            )
        endif()
    endif()
endfunction()

###########################################################################
# 测试目标：与原逻辑一致，确保无遗漏
###########################################################################
# C++测试
add_mpi_cpp_executable(test_mpi_cpp test_mpi.cpp)
add_mpi_cpp_executable(mpi_basic_cpp mpi_basic.cpp)
add_mpi_cpp_executable(mpi_sendrecv_demo mpi_sendrecv_demo.cpp)
add_mpi_cpp_executable(mpi_split_comm_index mpi_split_comm_index.cpp)
add_mpi_cpp_executable(test_all_mpi_cpp test_all_mpi.cpp)

# Fortran测试
add_mpi_fortran_executable(test_mpi_fortran test_mpi.F90)
add_mpi_fortran_executable(mpi_max_broadcast mpi_max_broadcast.F90)
add_mpi_fortran_executable(mpi_comm_split mpi_comm_split.F90)
add_mpi_fortran_executable(mpi_basic mpi_basic.F90)

# 混合语言测试
add_mpi_mixed_executable(mixed_c_fortran
    mixed_c_fortran.c
    mixed_c_fortran.f90
)
add_mpi_mixed_executable(mixed_fortran_c
    mixed_fortran_c.c
    mixed_fortran_c.f90
)

###########################################################################
# 复制测试脚本 +  Intel MPI专用运行说明
###########################################################################
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/run_all_mpi_tests.sh
    ${CMAKE_BINARY_DIR}/run_all_mpi_tests.sh
    COPYONLY
)
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/check_mpi_coverage.py
    ${CMAKE_BINARY_DIR}/check_mpi_coverage.py
    COPYONLY
)

# Intel MPI编译运行提示（关键！必须加载Intel环境）
message(STATUS "==================================================")
message(STATUS "Intel MPI 编译前请执行以下命令加载环境：")
message(STATUS "  1. 清理残留环境：unset LD_LIBRARY_PATH CPATH PKG_CONFIG_PATH")
message(STATUS "  2. 加载Intel MPI：source /public/software/mpi/intelmpi/5.0.2.044/bin/mpivars.sh intel64")
message(STATUS "  3. 验证环境：mpicxx --version（应显示Intel MPI）")
message(STATUS "编译命令：")
message(STATUS "  mkdir -p build-intelmpi && cd build-intelmpi")
message(STATUS "  cmake .. && make -j32")
message(STATUS "运行命令（Intel MPI专用）：")
message(STATUS "  export MPITRACER_LIB=\$PWD")
message(STATUS "  mpirun -n 4 -genv LD_PRELOAD=\$MPITRACER_LIB/mpitracer.so ./test_mpi_cpp")
message(STATUS "==================================================")
